package com.zwps.common.controller.advice;

import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import com.zwps.common.controller.model.vo.ParameterNotMeetRequireVO;
import com.zwps.common.controller.response.message.ResponseDataMessage;
import com.zwps.common.controller.response.message.ResponseMessage;
import com.zwps.common.core.context.RecordAPILogContext;
import com.zwps.common.core.exception.AbstractCommonException;
import com.zwps.common.core.exception.status.CommonExceptionStatus;
import com.zwps.common.core.exception.status.InterfaceCommonExceptionStatus;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {

    private void recordException(Exception exception) {
        RecordAPILogContext.getRequestLog().setExceptionDetail(ExceptionUtils.getStackTrace(exception));
    }
    
    @ExceptionHandler(value = Exception.class)
    public ResponseMessage exceptionHandle(Exception exception) {
        log.error("GlobalExceptionAdvice handleException: ", exception);
        recordException(exception);
        return ResponseMessage.serverExceptionMessage();
    }

//    /**
//     * 将feign调用出现的异常原样返回
//     * @Title: exceptionHandle
//     * @Description: 
//     * @param exception
//     * @return ResponseEntity<String>
//     * @author lyh
//     * @date 2023年8月16日 下午6:01:46
//     */
//    @ExceptionHandler(FeignException.class)
//    @ResponseBody
//    public ResponseEntity<String> exceptionHandle(FeignException exception) {
//        return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON)
//                .body(exception.getExceptionMessage());
//    }
    
    @ExceptionHandler(AbstractCommonException.class)
    public ResponseMessage abstractCommonExceptionHandle(AbstractCommonException exception) {
        log.error("GlobalExceptionAdvice abstractCommonExceptionHandle: ", exception);
        recordException(exception);
        InterfaceCommonExceptionStatus status = exception.getStatus();
        String message = status.getDescribe();
        if (exception.getMessage() != null) {
            message = exception.getMessage();
        }
        return new ResponseMessage(status.getCode(), message);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseMessage exceptionHandle(HttpRequestMethodNotSupportedException exception) {
        recordException(exception);
        CommonExceptionStatus status = CommonExceptionStatus.UNSUPPORTED_REQUEST_METHOD;
        return new ResponseMessage(status.getCode(), exception.getMessage());
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public ResponseMessage exceptionHandle(DuplicateKeyException exception) {
        recordException(exception);
        log.error("GlobalExceptionAdvice handleException: ", exception);
        // 匹配数据库报错信息
        String regex = "Duplicate entry '(.*?)' for key ";
//        String regex = "Duplicate entry '(.*?)' for key '(.*?)\\..*?_(.*?)_uindex'";
        Matcher matcher = Pattern.compile(regex).matcher(exception.getMessage());
        String message = "信息已经被占用";
        // 如果存在信息
        if (matcher.find()) {
            // 获取被占用的值
            String value = matcher.group(1);
            if (StrUtil.isNotEmpty(value)) {
                message = value + " 已经被占用";
            }
        }
        CommonExceptionStatus status = CommonExceptionStatus.DUPLICATE_DATA;
        return new ResponseMessage(status.getCode(), message);
    }

    /**
     * API方法参数校验不通过
     * 
     * @Title: constraintViolationExceptionHandler
     * @Description:
     * @param exception
     * @param request
     * @return ResponseDataMessage<List<ParameterNotMeetRequireVO>>
     * @author lyh
     * @date 2023年5月27日 下午6:07:48
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseDataMessage<List<ParameterNotMeetRequireVO>> constraintViolationExceptionHandler(
            ConstraintViolationException exception, HttpServletRequest request) {
        recordException(exception);
        log.error("GlobalExceptionAdvice constraintViolationExceptionHandler: ", exception);
        ArrayList<ParameterNotMeetRequireVO> list = new ArrayList<ParameterNotMeetRequireVO>();
        for (ConstraintViolation<?> v : exception.getConstraintViolations()) {

            // 参数名在集合的最后一个
            PathImpl pathImpl = (PathImpl) v.getPropertyPath();
            String name = pathImpl.makeLeafNodeIterable().getName();
            list.add(new ParameterNotMeetRequireVO(name, v.getMessage()));
        }
        return ResponseDataMessage.parameterNotMeetRequireMessage(list);
    }


    /**
     * 
     * @Title: validParameterExceptionHandler
     * @Description: 统一处理请求参数校验(实体对象传参-form、x-www-form-urlencoded)
     * @param exception
     * @param request
     * @return ResponseMessage
     * @throws
     */
    @ExceptionHandler({ BindException.class })
    public ResponseDataMessage<List<ParameterNotMeetRequireVO>> validParameterExceptionHandler(BindException exception,
            HttpServletRequest request) {
        recordException(exception);
        log.error("GlobalExceptionAdvice validParameterExceptionHandler: ", exception);
        ArrayList<ParameterNotMeetRequireVO> list = new ArrayList<ParameterNotMeetRequireVO>();
        for (FieldError error : exception.getBindingResult().getFieldErrors()) {
            list.add(new ParameterNotMeetRequireVO(error.getField(), error.getDefaultMessage()));
        }
        return ResponseDataMessage.parameterNotMeetRequireMessage(list);
    }

    /**
     * 方法参数校验未满足要求时抛出的异常 1.json数据，在类的属性中添加校验注解时触发
     * 
     * @param exception
     * @return
     */
    @ExceptionHandler({ MethodArgumentNotValidException.class })
    public ResponseDataMessage<List<ParameterNotMeetRequireVO>> methodArgumentNotValidExceptionHandler(
            MethodArgumentNotValidException exception) {
        recordException(exception);
        log.error("GlobalExceptionAdvice methodArgumentNotValidExceptionHandler: ", exception);
        List<FieldError> fieldErrors = exception.getBindingResult().getFieldErrors();
        ArrayList<ParameterNotMeetRequireVO> list = new ArrayList<ParameterNotMeetRequireVO>();
        for (FieldError field : fieldErrors) {
            list.add(new ParameterNotMeetRequireVO(field.getField(), field.getDefaultMessage()));
        }
        return ResponseDataMessage.parameterNotMeetRequireMessage(list);
    }

    /**
     * 无法解析参数异常
     * 
     * @Title: httpMessageNotReadableHandler
     * @Description:
     * @param exception
     * @param request
     * @return ResponseMessage
     * @author lyh
     * @date 2023年5月29日 上午10:14:09
     */
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public ResponseMessage httpMessageNotReadableHandler(HttpMessageNotReadableException exception,
            HttpServletRequest request) {
        recordException(exception);
        log.error("httpMessageNotReadableHandler: ", exception);
        if (exception.getRootCause() != null && DateTimeParseException.class == exception.getRootCause().getClass()) {
            return ResponseMessage.parameterErrorMessage("日期格式错误");
        }
        return ResponseMessage.parameterErrorMessage();
    }
    
    /**
     * 虽然这个捕捉了文件大小超出限制，但是只是偶尔正常把错误信息返回，猜测可能是这个是异步处理的
     * @Title: handleMaxUploadSizeExceededException
     * @Description: 
     * @param ex
     * @return ResponseEntity<ResponseMessage>
     * @author lyh
     * @date 2023年8月16日 下午3:56:19
     */
    @ExceptionHandler(value = MaxUploadSizeExceededException.class)
    @ResponseBody
    public ResponseEntity<ResponseMessage> handleMaxUploadSizeExceededException(MaxUploadSizeExceededException ex) {
        recordException(ex);
        ResponseMessage responseMessage =  ResponseMessage.fileSizeExceedsMaximumLimitMessage();
        return ResponseEntity.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON).body(responseMessage);
    }

//    /**
//     * 
//     * @Title: getRequestParam
//     * @Description: 获取request中的参数，转JSON字符串返回
//     * @param request
//     * @return String
//     * @throws
//     */
//    public static String getRequestParam(HttpServletRequest request) {
//        JSONObject jsonObj = new JSONObject();
//        Enumeration<?> enu = request.getParameterNames();
//        while (enu.hasMoreElements()) {
//            String paraName = (String) enu.nextElement();
//            jsonObj.put(paraName, request.getParameter(paraName));
//        }
//        return "{}".equals(jsonObj.toJSONString()) ? "" : jsonObj.toJSONString();
//    }
//
//    public void printErrorLog(String message, String error, HttpServletRequest request) {
//        StringBuilder string = new StringBuilder().append("message: ").append(message).append(", url: ")
//                .append(request.getServletPath()).append(", param: ").append(getRequestParam(request));
//        logger.error(string.toString());
//    };
    /**
     * 处理时间格式解析异常
     * @Title: dateTimeParseExceptionHandler
     * @Description: 处理LocalDateTime等时间类型参数转换异常
     * @param exception
     * @return ResponseMessage
     */
    @ExceptionHandler(DateTimeParseException.class)
    public ResponseMessage dateTimeParseExceptionHandler(DateTimeParseException exception) {
        recordException(exception);
        log.warn("时间格式解析失败: {}", exception.getMessage());
        return ResponseMessage.parameterErrorMessage("时间格式不正确，请使用 yyyy-MM-dd HH:mm:ss 格式");
    }

    /**
     * 处理参数类型不匹配异常
     * @Title: methodArgumentTypeMismatchExceptionHandler
     * @Description: 处理参数类型转换异常
     * @param exception
     * @return ResponseMessage
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseMessage methodArgumentTypeMismatchExceptionHandler(MethodArgumentTypeMismatchException exception) {
        recordException(exception);
        log.warn("参数类型不匹配: {}", exception.getMessage());

        String message;
        if (exception.getRequiredType() != null &&
                exception.getRequiredType().getName().contains("LocalDateTime")) {
            message = "参数 '" + exception.getName() + "' 时间格式不正确，请使用 yyyy-MM-dd HH:mm:ss 格式";
        } else {
            message = String.format("参数 '%s' 类型不匹配，期望类型: %s",
                    exception.getName(),
                    exception.getRequiredType() != null ? exception.getRequiredType().getSimpleName() : "未知");
        }

        return ResponseMessage.parameterErrorMessage(message);
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseMessage illegalArgumentExceptionHandler(IllegalArgumentException exception) {
        recordException(exception);
        log.warn("参数非法: {}", exception.getMessage());
        return ResponseMessage.parameterErrorMessage(exception.getMessage());
    }
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseMessage missingServletRequestParameterExceptionHandler(MissingServletRequestParameterException exception) {
        recordException(exception);
        log.warn("缺少必需参数: {}", exception.getMessage());

        String message = String.format("缺少必需参数: %s", exception.getParameterName());
        return ResponseMessage.parameterErrorMessage(message);
    }
}
